import socket
from threading import Thread
import time


class Manager:
	def __init__(self, soc: socket.socket, addr, username=''):
		"""
		初始化服务器实例，一个实例对应一个客户端提供服务。
		:param soc: 服务器的socket对象
		:param addr: 连接者属性（IP、端口等）
		:param username: 连接者的名称（需要后续输入处理，初始化时置空）
		"""
		self._ip = addr[0]
		self._port = addr[1]
		self._socket = soc
		self._username = username

	def getip(self):
		"""
		统合服务器实例的IP和端口属性，便于发送信息
		:return: IP-端口号的字符串
		"""
		return f"{self._ip}-{self._port}"

	def send(self, message, name):
		"""
		向用户端发送信息的通用函数。
		:param message: 信息文本
		:param name: 发送人的名称
		:return: 发送是否成功（bool变量）
		"""
		try:
			self._socket.send(
				f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))}){name}: {message}".encode('utf-8')
			)
			return True
		except:
			return False

	def receive(self):
		"""
		接收用户端发来信息的通用函数。其中特别的在检测到退出指令quit时传出停止连接的信号。
		:return: 发来信息的内容
		"""
		try:
			message = self._socket.recv(1024).decode('utf-8')
			if message == 'quit' or not message:
				return False
			return message
		except:
			return False

	def close_server(self):
		"""
		关闭用户端对应的服务器。
		:return: 关闭是否成功（Bool变量）
		"""
		try:
			self._socket.close()
			return True
		except:
			return False

	def client_thread(self):
		"""
		用户端处理的主线程。全部执行完毕时，退出。
		:return:
		"""
		try:
			file = open("log.txt", 'a', encoding='utf-8')
			print(f"{self.getip()}正在连接")
			file.write(f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))}){self.getip()}正在连接\n")
			# 连接用户端
			name_data = self.receive()
			if not name_data:
				return
			self._username = name_data
			print(f'用户{self._username}({self.getip()})，已连接')
			file.write(
				f'({str(time.strftime("%Y/%m/%d %H:%M:%S"))})用户{self._username}({self.getip()})，已连接\n'
			)
			self._socket.send("用户已连接".encode('utf-8'))
			for client in clients.values():
				client.send(message=f"{self._username}已进入", name="system")
			clients[c.getip()] = c
			clients_ports[self._username] = self.getip()
			# 获取用户名称信息，建立完整连接并广播。以下将处理信息的收发。

			while True:
				msg_data = self.receive()
				if not msg_data:
					# 发来的非信息，退出
					break
				elif msg_data.split(' ')[0] == '@':
					# 发来私信类消息，进行定向转发
					toward = msg_data.split(' ')[1]
					message_text = msg_data.split(' ')[2]
					self.send(msg_data, self._username)
					clients[clients_ports[toward]].send(message=msg_data, name=self._username)
					# 回传给发送者、转发给接受者
					file.write(
						f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))})用户{self._username}向{toward}发送私信：{message_text}\n"
					)
					print(f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))}){self._username}：{msg_data}")
				else:
					print(f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))}){self._username}：{msg_data}")
					file.write(
						f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))})用户{self._username}发送：{msg_data}\n"
					)
					for client in clients.values():
						client.send(message=msg_data, name=self._username)
					# 向连接到服务端的所有用户（包括对发送者的回传）广播转发信息

		except socket.error as se:
			print(f"Socket Error...{str(se)}")
		except Exception as exc:
			print(f"Other Exception...{str(exc)}")
		finally:
			print(f"{self.getip()}断开连接")
			file.write(f"({str(time.strftime('%Y/%m/%d %H:%M:%S'))}){self.getip()}断开连接\n")
			self.close_server()
			clients.pop(self.getip())
			clients_ports.pop(self._username)
			for client in clients.values():
				client.send(message=f"{self._username}已离开", name="system")
			# 断开连接，关闭服务，将用户从用户列表中弹出并周知其他用户


if __name__ == "__main__":
	clients = dict()
	clients_ports = dict()
	# 全局变量，储存用户信息和IP信息。
	serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	host = "192.168.43.111"
	port = 2121
	serv.bind((host, port))
	serv.listen(10)
	print(f"正在监听服务器{serv.getsockname()}状态...")
	while True:
		conn, addr = serv.accept()
		c = Manager(conn, addr)
		t = Thread(target=Manager.client_thread, args=(c, ))
		t.start()
